﻿using Microsoft.Xna.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UCS.XNA.Utils;

namespace UCS.XNA.CombatBiplanes.Synchronization
{
    public class CommandConverter
    {
        #region Constants

        private const string FIRE_REGEX = "<A[0-9]{2}FIRE>";
        private const string ANGLE_REGEX = "<A[0-9]{2}ANGLE@[0-9]{3}>";
        private const string EXPLODE_REGEX = "<[AR][0-9]{2}EXPLODE>";
        private const string MOVE_REGEX = "<A[0-9]{2}MOVE>";
        private const string START_REGEX = "<S00START>";
        private const string END_REGEX = "<S00END>";
        private const string CONNECT_REGEX = "<S00CONNECT@[0-9]{2}>";
        private const string YOUARE_REGEX = "<S00YOUARE@[0-9]{2}>";
        private const string INIT_REGEX = "<S00INIT@[0-9]{10}>";
        private const string FIN_REGEX = "<S00FIN@[0-9]{2}>";
        private const string LIFE_REGEX = "<S00LIFE@[0-9]{2}>";

        #endregion

        #region Attributes and Properties

        private List<Airplane> _airplanes;
        public List<Airplane> Airplanes
        {
            get { return this._airplanes; }
            set { this._airplanes = value; }
        }

        private RefreshableCollection<Rocket> _rockets;
        public RefreshableCollection<Rocket> Rockets
        {
            get { return this._rockets; }
            set { this._rockets = value; }
        }

        private CombatBiplanes _game;
        public CombatBiplanes Game
        {
            get { return this._game; }
            set { this._game = value; }
        }

        #endregion

        #region Constructors

        public CommandConverter(CombatBiplanes game)
        {
            this._airplanes = new List<Airplane>();
            this._rockets = new RefreshableCollection<Rocket>();
            this._game = game;
        }

        public CommandConverter(List<Airplane> airplanes, RefreshableCollection<Rocket> rockets, CombatBiplanes game)
        {
            this._airplanes = airplanes;
            this._rockets = rockets;
            this._game = game;
        }

        #endregion

        #region Public Methods

        public byte[] ToCommand(ActionType actionType, IActionExecutor executor, int[] parameters)
        {
            BitArray bits;
            if (parameters == null)
                bits = new BitArray(9);
            else
                bits = new BitArray(9 + parameters.Count() * 10);

            if (executor is Airplane)
            {
                bits.Set(0, true);
                bits.Set(1, false);
            }
            else if (executor is Rocket)
            {
                bits.Set(0, false);
                bits.Set(1, true);
            }
            else
            {
                bits.Set(0, false);
                bits.Set(1, false);
            }

            BitArray number = new BitArray(new int[] { executor.ID });
            bits.Set(2, number.Get(0));
            bits.Set(3, number.Get(1));
            bits.Set(4, number.Get(2));

            BitArray type = new BitArray(new int[] { (int)actionType });
            bits.Set(5, type.Get(0));
            bits.Set(6, type.Get(1));
            bits.Set(7, type.Get(2));
            bits.Set(8, type.Get(3));

            if (parameters != null)
            {
                for (int c = 0; c < parameters.Count(); c++)
                {
                    BitArray parameter = new BitArray(new int[] { parameters[c] });

                    for (int i = 0; i < 10; i++)
                        bits.Set(i + 9 + 10 * c, parameter.Get(i));
                }
            }

            byte[] bytes = new byte[5];
            bits.CopyTo(bytes, 0);

            return bytes;
        }

        public ActionType ToActionType(byte[] bytes)
        {
            BitArray allBits = new BitArray(bytes);
            BitArray bits = new BitArray(4);
            bits.Set(0, allBits.Get(5));
            bits.Set(1, allBits.Get(6));
            bits.Set(2, allBits.Get(7));
            bits.Set(3, allBits.Get(8));

            int[] number = new int[1];
            bits.CopyTo(number, 0);

            return (ActionType)number[0];
        }

        public IActionExecutor ToActionExecutor(byte[] bytes)
        {
            BitArray allBits = new BitArray(bytes);
            BitArray typeBits = new BitArray(2);
            typeBits.Set(0, allBits.Get(0));
            typeBits.Set(1, allBits.Get(1));

            int[] typeNumber = new int[1];
            typeBits.CopyTo(typeNumber, 0);

            BitArray numberBits = new BitArray(3);
            numberBits.Set(0, allBits.Get(2));
            numberBits.Set(1, allBits.Get(3));
            numberBits.Set(2, allBits.Get(4));

            int[] number = new int[1];
            numberBits.CopyTo(number, 0);

            switch (typeNumber[0])
            {
                case 1:
                    return this._airplanes.Find(a => a.ID == number[0]);
                case 2:
                    return this._rockets.First(r => r.ID == number[0]);
                case 0:
                    return this._game;
                default:
                    throw new InvalidCommandException();
            }
        }

        public int[] ToParameter(byte[] bytes)
        {
            BitArray allBits = new BitArray(bytes);
            BitArray bits = new BitArray(10);
            int parameterCount = (allBits.Count - 9) / 10;
            int[] parameters = new int[parameterCount];

            for (int c = 0; c < parameterCount; c++)
            {
                for (int i = 0; i < 10; i++)
                    bits.Set(i, allBits.Get(i + 9 + 10 * c));
                bits.CopyTo(parameters, c);
            }

            if (parameterCount > 0)
                return parameters;
            else
                return null;
        }

        #endregion
    }
}
